#include "../corelib/util/nine_grid_scene.hh"
#include "unittest/unittest.hh"
#include <cstdint>

using namespace kratos::corelib;

FIXTURE_BEGIN(NineGridScene)

CASE(TestInvalidArgument) {
    bool except = false;
    try {
        NineGridScene<std::uint64_t> scene(10, 8, 8);
    } catch (std::exception&) {
        except = true;
    }
    ASSERT_TRUE(except);
}

CASE(TestEnter1) {
    bool result1 = false;
    bool result2 = false;
    int time = 0;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 33, 44, nullptr);
    scene.enter(2, 35, 46,
        [&](std::uint64_t from, std::uint64_t to) {
            if (from == 1 && to == 2) {
                result1 = true;
                time += 1;
            }
            if (from == 2 && to == 1) {
                result2 = true;
                time += 1;
            }
        }
    );
    ASSERT_TRUE(result1 && result2);
    ASSERT_TRUE(time == 2);
}

CASE(TestEnter2) {
    bool result1 = false;
    bool result2 = false;
    int time = 0;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 9, 9,
        [&](std::uint64_t from, std::uint64_t to) {
            if (from == 1 && to == 2) {
                result1 = true;
                time += 1;
            }
            if (from == 2 && to == 1) {
                result2 = true;
                time += 1;
            }
        }
    );
    ASSERT_TRUE(result1 && result2);
    ASSERT_TRUE(time == 2);
}

CASE(TestEnter3) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 29, 29,
        [&](std::uint64_t from, std::uint64_t to) {
            if (from == 1 && to == 2) {
                result1 = true;
            }
            if (from == 2 && to == 1) {
                result2 = true;
            }
        }
    );
    ASSERT_TRUE(result1 && result2);
}

CASE(TestEnter4) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 9, 29,
        [&](std::uint64_t from, std::uint64_t to) {
            if (from == 1 && to == 2) {
                result1 = true;
            }
            if (from == 2 && to == 1) {
                result2 = true;
            }
        }
    );
    ASSERT_TRUE(result1 && result2);
}

CASE(TestEnter5) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 29, 9,
        [&](std::uint64_t from, std::uint64_t to) {
            if (from == 1 && to == 2) {
                result1 = true;
            }
            if (from == 2 && to == 1) {
                result2 = true;
            }
        }
    );
    ASSERT_TRUE(result1 && result2);
}

CASE(TestEnter6) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 19, 29,
        [&](std::uint64_t from, std::uint64_t to) {
            if (from == 1 && to == 2) {
                result1 = true;
            }
            if (from == 2 && to == 1) {
                result2 = true;
            }
        }
    );
    ASSERT_TRUE(result1 && result2);
}

CASE(TestEnter7) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 9, 19,
        [&](std::uint64_t from, std::uint64_t to) {
            if (from == 1 && to == 2) {
                result1 = true;
            }
            if (from == 2 && to == 1) {
                result2 = true;
            }
        }
    );
    ASSERT_TRUE(result1 && result2);
}

CASE(TestEnter8) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 30, 30,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(!result1 && !result2);
}

CASE(TestLeave1) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 33, 44, nullptr);
    scene.enter(2, 35, 46,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(result1 && result2);
    bool leave = false;
    scene.leave(1, 33, 44,
        [&](std::uint64_t from, std::uint64_t to) {
            if (from == 1 && to == 2) {
                leave = true;
            }
        }
    );
    ASSERT_TRUE(leave);
}

CASE(TestLeave2) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 9, 9,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(result1 && result2);
    bool leave = false;
    scene.leave(1, 19, 19,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            leave = true;
        }
    }
    );
    ASSERT_TRUE(leave);
}

CASE(TestLeave3) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 29, 29,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(result1 && result2);
    bool leave = false;
    scene.leave(1, 19, 19,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            leave = true;
        }
    }
    );
    ASSERT_TRUE(leave);
}

CASE(TestLeave4) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 9, 29,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(result1 && result2);
    bool leave = false;
    scene.leave(1, 19, 19,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            leave = true;
        }
    }
    );
    ASSERT_TRUE(leave);
}

CASE(TestLeave5) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 29, 9,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(result1 && result2);
    bool leave = false;
    scene.leave(1, 19, 19,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            leave = true;
        }
    }
    );
    ASSERT_TRUE(leave);
}

CASE(TestLeave6) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 19, 29,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(result1 && result2);
    bool leave = false;
    scene.leave(1, 19, 19,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            leave = true;
        }
    }
    );
    ASSERT_TRUE(leave);
}

CASE(TestLeave7) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 9, 19,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(result1 && result2);
    bool leave = false;
    scene.leave(1, 19, 19,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            leave = true;
        }
    }
    );
    ASSERT_TRUE(leave);
}

CASE(TestMove1) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 19, 19, nullptr);
    scene.enter(2, 9, 19,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(result1 && result2);
    result1 = false;
    result2 = false;
    scene.move(2, 9, 19, 10, 20, nullptr, nullptr, 
        [&](std::uint64_t from, std::uint64_t to) {
            if (from == 1 && to == 2) {
                result1 = true;
            }
            if (from == 2 && to == 1) {
                result2 = true;
            }
        }
    );
    ASSERT_TRUE(!result1 && result2);
}

CASE(TestMove2) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 20, 20, nullptr);
    scene.enter(2, 1, 1,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(!result1 && !result2);
    result1 = false;
    result2 = false;
    scene.move(2, 1, 1, 10, 10,
        [&](std::uint64_t from, std::uint64_t to) {
            if (from == 1 && to == 2) {
                result1 = true;
            }
            if (from == 2 && to == 1) {
                result2 = true;
            }
        },
        nullptr, nullptr);
    ASSERT_TRUE(result1 && result2);
}

CASE(TestMove3) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 20, 20, nullptr);
    scene.enter(2, 11, 11,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(result1 && result2);
    result1 = false;
    result2 = false;
    scene.move(2, 11, 11, 1, 1,nullptr,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }, nullptr);
    ASSERT_TRUE(result1 && result2);
}

CASE(TestQueryCicle1) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 20, 20, nullptr);
    scene.enter(2, 11, 11,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(result1 && result2);
    result1 = false;
    scene.queryCircle(2, 11, 11, 20,
        [&](std::uint64_t target, std::int32_t x, std::int32_t y, std::int32_t radius) {
            if (target == 1) {
                result1 = true;
            }
            return true;
        }
    );
    ASSERT_TRUE(result1);
}

CASE(TestQueryCicle2) {
    bool result1 = false;
    bool result2 = false;
    NineGridScene<std::uint64_t> scene(10, 100, 100);
    scene.enter(1, 20, 20, nullptr);
    scene.enter(2, 11, 11,
        [&](std::uint64_t from, std::uint64_t to) {
        if (from == 1 && to == 2) {
            result1 = true;
        }
        if (from == 2 && to == 1) {
            result2 = true;
        }
    }
    );
    ASSERT_TRUE(result1 && result2);
    result1 = false;
    scene.queryCircle(2, 11, 11, 5,
        [&](std::uint64_t target, std::int32_t x, std::int32_t y, std::int32_t radius) {
        if (target == 1) {
            result1 = true;
        }
        return true;
    }
    );
    ASSERT_TRUE(!result1);
}

FIXTURE_END(NineGridScene)
